package com.onpositive.data.serializer.readers;

import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;

import com.onpositive.data.AbstractReader;
import com.onpositive.data.FilterTester;
import com.onpositive.data.IWritable;
import com.onpositive.data.serializer.writers.AbstractWriter;
import com.onpositive.data.serializer.writers.StringWriter;

public class StringReader extends AbstractReader<String> implements IWritable {

	public static final int DIRECT_STORE = StringWriter.DIRECT_STORE;
	public static final int INDIRECT_STORE = StringWriter.INDIRECT_STORE;

	private static final int INT_SIZE = 4;
	private static final int CHAR_SIZE = 2;

	private int sizeInBytes, storageType, size, storageSize;
	private StringBuilder builder = new StringBuilder();

	private int[] offsets, indexSequence;

	public StringReader() {
		super(String.class);
	}
	@Override
	public AbstractWriter<String> getWriter() {
		return new StringWriter();
	}

	protected HashMap<Integer, Object> rewrites;

	@Override
	public String getObject(int i) {

		if (!isInitialized())
			init();
		if (rewrites != null) {
			if (rewrites.containsKey(i)) {
				return (String) rewrites.get(i);
			}
		}
		int j;
		if (storageType == DIRECT_STORE)
			j = i;
		else if (storageType == INDIRECT_STORE) {
			j = indexSequence[i];
			if (j == 0)
				return null;
		} else {
			// throw horrible error ;
			return null;
		}

		try {
			int valueStart = offsets[j];
			//
			this.file.seek(collectionStart + myStart + valueStart);

			// int valueLength = offsets[j+1] - valueStart ;
			// byte bArr[] = new byte[ valueLength ] ;
			// file.read( bArr, 0, valueLength ) ;
			// ByteBuffer bBuf = ByteBuffer.wrap( bArr ) ;
			// CharBuffer cBuf = bBuf.asCharBuffer() ;
			//
			// builder.delete( 0, builder.length() ) ;
			//
			// for( int k = 0 ; k < (valueLength >> 1 ) ; k++ )
			// builder.append( cBuf.get() ) ;
			//
			// String result = builder.toString() ;

			String result = file.readUTF();
			return result;

		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}

	@Override
	protected void doInit() {

		int initialReadSize = INT_SIZE * 4;

		try {
			this.file.seek(collectionStart + myStart + INT_SIZE); // also skip
																	// writerID

			byte[] byteArr = new byte[initialReadSize];

			file.read(byteArr, 0, initialReadSize);
			ByteBuffer bBuf = ByteBuffer.wrap(byteArr);
			IntBuffer iBuf = bBuf.asIntBuffer();

			sizeInBytes = iBuf.get();
			storageType = iBuf.get();
			size = iBuf.get();

			if (storageType == DIRECT_STORE) {
				int offsetsSize = size + 1;
				offsets = new int[offsetsSize];

				byteArr = new byte[INT_SIZE * offsetsSize];
				file.read(byteArr, 0, INT_SIZE * offsetsSize);
				bBuf = ByteBuffer.wrap(byteArr);
				iBuf = bBuf.asIntBuffer();

				for (int i = 0; i < offsetsSize; i++)
					offsets[i] = iBuf.get();

				return;
			}
			if (storageType == INDIRECT_STORE) {
				storageSize = iBuf.get();

				int offsetsSize = storageSize + 1;
				offsets = new int[offsetsSize];

				byteArr = new byte[INT_SIZE * offsetsSize];
				file.read(byteArr, 0, INT_SIZE * offsetsSize);
				bBuf = ByteBuffer.wrap(byteArr);
				iBuf = bBuf.asIntBuffer();

				for (int i = 0; i < offsetsSize; i++)
					offsets[i] = iBuf.get();

				indexSequence = new int[size];

				byteArr = new byte[INT_SIZE * size];
				file.read(byteArr, 0, INT_SIZE * size);
				bBuf = ByteBuffer.wrap(byteArr);
				iBuf = bBuf.asIntBuffer();

				for (int i = 0; i < size; i++)
					indexSequence[i] = iBuf.get();

				return;
			}

			// here we throw horrible error

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void filterOut(LinkedHashSet<Object> obj, String text) {
		if (!isInitialized())
			init();
		HashMap<Integer, Boolean> bs = new HashMap<Integer, Boolean>();
		for (Iterator iterator = obj.iterator(); iterator.hasNext();) {
			Number object = (Number) iterator.next();
			int k = object.intValue();
			if (!match(k, text, bs)) {
				iterator.remove();
			}
		}
	}

	private boolean match(int i, String text, HashMap<Integer, Boolean> bs) {
		if (!isInitialized())
			init();

		int j;
		if (storageType == DIRECT_STORE)
			j = i;
		else if (storageType == INDIRECT_STORE) {
			j = indexSequence[i];
			if (j == 0)
				return false;
		} else {
			// throw horrible error ;
			return false;
		}

		try {
			int valueStart = offsets[j];
			Boolean boolean1 = bs.get(valueStart);
			if (boolean1 != null) {
				return boolean1;
			}
			//
			this.file.seek(collectionStart + myStart + valueStart);

			// int valueLength = offsets[j+1] - valueStart ;
			// byte bArr[] = new byte[ valueLength ] ;
			// file.read( bArr, 0, valueLength ) ;
			// ByteBuffer bBuf = ByteBuffer.wrap( bArr ) ;
			// CharBuffer cBuf = bBuf.asCharBuffer() ;
			//
			// builder.delete( 0, builder.length() ) ;
			//
			// for( int k = 0 ; k < (valueLength >> 1 ) ; k++ )
			// builder.append( cBuf.get() ) ;
			//
			// String result = builder.toString() ;

			String result = file.readUTF();
			int indexOf = result.indexOf(text);
			boolean ms = indexOf != -1;
			bs.put(valueStart, ms);
			return ms;

		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
	}

	@Override
	public boolean accept(int op, int a, Object constraint) {
		
		if (op == FilterTester.EQ) {
			String object = getObject(a);
			if (object != null) {
				object.toString().equals(constraint);
			}
		}
		if (op == FilterTester.CONTAINS) {
			String object = getObject(a);
			if (constraint==null){
				return object==null;
			}
			if (object != null) {
				String string = object.toString().toLowerCase();
				String string2 = constraint.toString().toLowerCase();
				return string.replace(' ', '_').contains(
						string2.replace(' ', '_'));
			}
			return constraint == null;
		}
		if (op == FilterTester.ONE_OF) {
			Collection<?>n=(Collection<?>) constraint;
			String object = getObject(a);
			if (object != null) {
				for (Object q:n){
					if (object.toString().toLowerCase().contains(q.toString().toLowerCase())){
						return true;
					}
				}
				
			}
			return constraint == null;
		}
		throw new IllegalStateException();
	}

	@Override
	public void setValue(int index, Object value) {
		if (rewrites == null) {
			rewrites = new HashMap<Integer, Object>();

		}
		rewrites.put(index, value==null?null:value.toString());
	}

	@Override
	public void serialize(DataOutputStream ds) throws IOException {
		StringWriter v = new StringWriter();
		for (int a = 0; a < offsets.length; a++) {
			v.append(getObject(a));
		}
		v.serialize(ds);
	}

	

	@Override
	public Object aggregate(int op, BitSet ss) {
		String str=null;
		if (op==AGR_IDE){
			for (int a=0;a<offsets.length;a++){
				if (ss.get(a)){
					if( str==null){
						str=getObject(a);
					}
					else{
						String object = getObject(a);
						if (object!=null&&object!=str){
							return null;
						}
					}
				}
			}
		}		
		return str;
	}
	
	@Override
	public void initEmpty(int dataSize) {
		setInitialized();
	}
	@Override
	public void expand(int newSize) {
		
	}
}
